<?php

namespace Lib\Socket;

class Socket {
	private $_aConn = array ();
	private $_sDefault = null;
	private $_iErrorCode = null;
	private $_sErrorMessage = null;
	private $_iTimeoutSec = 1;
	private $_iTimeoutUsec = 0;

	public function __construct() {
		try {
			if (! extension_loaded ( 'socket' )) {
				new ExceptionExt ( '缺少SOCKET扩展' );
			}
		} catch ( ExceptionExt $e ) {
			$e->showError ();
		}
	}

	public function __call($sName, $aParam) {
		$sType = $this->_sDefault;
		if (! $this->$sType) {
		}
		return call_user_func_array ( array (
				$this->$sType,
				$sName 
		), $aParam );
	}

	public function __get($sName) {
		$sName = $sName == 'Server' ? 'Server' : 'Client';
		if (! $this->$sName) {
			require_once __DIR__ . '/' . $sName . '.class.php';
			$sClass = __NAMESPACE__ . '\\' . $sName;
			$this->__set ( $sName, new $sClass () );
		}
		return $this->$sName;
	}

	public function __set($sName, $sValue) {
		$this->$sName = $sValue;
	}

	/**
	 * socket建立连接
	 *
	 * @param 主机地址 $sHost        	
	 * @param 端口 $iPort        	
	 * @param 协议类型 $sProtocol        	
	 * @return multitype: boolean resource
	 */
	public function connect($sHost, $iPort = 0, $sProtocol = 'tcp') {
		$iProtocol = getprotobyname ( $sProtocol );
		if ($iProtocol == 17) {
			$iType = SOCK_DGRAM;
		} elseif ($iProtocol == 1) {
			$iType = SOCK_RAW;
		} else {
			$iType = SOCK_STREAM;
		}
		$sConnName = md5 ( $iProtocol . '_' . $sHost . ':' . $iPort );
		if (isset ( $this->_aConn [$sConnName] )) {
			$this->_sDefault = $sConnName;
			return $this->_aConn [$sConnName];
		} else {
			if (! @$rSocket = socket_create ( AF_INET, $iType, $iProtocol )) {
				$this->_iErrorCode = socket_last_error ();
				$this->_sErrorMessage = socket_strerror ( $this->_iErrorCode );
				return false;
			}
			socket_set_option ( $rSocket, SOL_SOCKET, SO_RCVTIMEO, array (
					"sec" => $this->_iTimeoutSec,
					"usec" => $this->_iTimeoutUsec 
			) );
			socket_set_option ( $rSocket, SOL_SOCKET, SO_SNDTIMEO, array (
					"sec" => $this->_iTimeoutSec,
					"usec" => $this->_iTimeoutUsec 
			) );
			$iTimeout = time () + $this->_iTimeoutSec;
			while ( ! @socket_connect ( $rSocket, $sHost, $iPort ) ) {
				$this->_iErrorCode = socket_last_error ();
				$this->_sErrorMessage = socket_strerror ( $this->_iErrorCode );
				socket_close ( $rSocket );
				return false;
			}
			
			$this->_sDefault = $sConnName;
			$this->_aConn [$sConnName] = $rSocket;
			return $sConnName;
		}
	}

	/**
	 * 获取连接
	 *
	 * @param 连接ID $sId        	
	 * @return resource
	 */
	public function get($sId = null) {
		if ($sId && $this->_aConn [$sId]) {
			return $this->_aConn [$sId];
		} else {
			return $this->_aConn [$this->_sDefault];
		}
	}

	/**
	 * socket关闭连接
	 *
	 * @param 连接ID $sId        	
	 */
	public function close($sId = null) {
		socket_close ( $this->get ( $sId ) );
		if ($sId == $this->_sDefault) {
			unset ( $this->_aConn [$this->_sDefault] );
			$this->_sDefault = key ( $this->_aConn );
		}
	}

	/**
	 * 返回最后一条错误
	 *
	 * @param 连接ID $sId        	
	 * @return array
	 */
	public function getError($sId = null) {
		$iCode = @socket_last_error ( $this->get ( $sId ) );
		if (! $iCode) {
			$iCode = $this->_iErrorCode;
		}
		$sMessage = socket_strerror ( $iCode );
		return array (
				'code' => $iCode,
				'message' => $sMessage 
		);
	}

	/**
	 * 设置超时时间
	 *
	 * @param 秒 $iSec        	
	 * @param 毫秒 $iUsec        	
	 */
	public function setTimeout($iSec, $iUsec) {
		$this->_iTimeoutSec = $iSec;
		$this->_iTimeoutUsec = $iUsec;
	}

	/**
	 * 设置错误
	 *
	 * @param 错误代码 $iCode        	
	 * @param 错误信息 $sMessage        	
	 */
	public function setError($iCode, $sMessage) {
		$this->_iErrorCode = $iCode;
		$this->_sErrorMessage = $sMessage;
	}
}

?>